home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1997 September / Macworld (1997-09).dmg / Shareware World / Comms & Internet / Mail*Link SMTP⁄QM Installer / Disk 3 / Sources & Scripts / Sources / single.c next >
C/C++ Source or Header  |  1994-05-15  |  46KB  |  1,904 lines

  1. /*
  2.     File:        single.c
  3.  
  4.     Contains:    xxx put contents here xxx
  5.  
  6.     Written by:    Tom Biddulph
  7.  
  8.     Copyright:    © 1992 by StarNine Technologies, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.       <1.10>    10/29/93    cac        Add in the extended finder info to the finder field.
  13.        <1.9>    10/20/93    cac        Make single.c create things in version2 applesingle.
  14.        <1.8>     6/23/93    cac        Fix a joe bug from 4 years ago. Don't assume TOPS, always seek
  15.                                     to the offet for writing the resoruce and don't assume we padded
  16.                                     all entries to that point.
  17.        <1.7>    12/16/92    bid        Some systems are aligning our structure declarations on long
  18.                                     word boundaries. This makes the "sizeof" operator return the
  19.                                     wrong number of bytes when used explicitly to get the structure
  20.                                     of the APPLESINGLE header. Instead, use a define that is the sum
  21.                                     of the sizes of the elements of the structures in question.
  22.                 10/20/92    bid        This is the original version of
  23.                                     "single.c". It has been moved from
  24.                                     UNIX/SCCS control to MPW projector
  25.                                     control.
  26.  
  27.     To Do:
  28. */
  29.  
  30. /* @(#)(C) Copyright 1993 StarNine Technologies. All rights reserved. */
  31.  
  32. static char sccs_ident[] ="@(#)Copyright StarNine Technologies 1993\t\
  33. Version 1.9 of single.c on 93/03/09 08:53:42";
  34. /*
  35. ** Copyright (c) 1988,1989 StarNine Technologies, Inc.
  36. ** All rights reserved.
  37. **
  38. ** Redistribution and use in source and binary forms are permitted
  39. ** provided that the above copyright notice and this paragraph are
  40. ** duplicated in all such forms and that any documentation,
  41. ** advertising materials, and other materials related to such
  42. ** distribution and use acknowledge that the software was developed
  43. ** by StarNine Technologies, Inc.
  44. **
  45. ** Mail*Link and StarNine are Trademarks of StarNine Technologies, Inc.
  46. ** StarNine Technologies, Inc.'s corporate name and/or its Trademarks
  47. ** CANNOT be used to endorse or promote products derived from
  48. ** this software without specific prior written permission signed by a
  49. ** corporate officer of StarNine Technologies, Inc. authorizing their use.
  50. **
  51. ** THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
  52. ** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  53. ** WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  54. ** THIS SOFTWARE IS NOT WARRANTED TO BE ERROR FREE.
  55. */
  56.  
  57.  
  58.  
  59. /*
  60. ** single
  61. ** USAGE
  62. **    single [-o outputfile] [-s] [-c Creator] [-t Type] inputfile
  63. **
  64. ** This program will split an APPLESINGLE file into data and resource forks.
  65. ** It will also translate "PACKIT 1" format files.
  66. **
  67. */
  68. #include    <stdio.h>
  69. #include    <fcntl.h>
  70. #include    <string.h>
  71. #include     <ctype.h>
  72. #include    <sys/types.h>
  73. #include    <sys/stat.h>
  74. #include    <sys/param.h>
  75. #include    <errno.h>
  76. #include    <netinet/in.h>
  77.  
  78. #ifndef    MAXNAMLEN
  79. #include    <dirent.h>
  80. #endif    /* MAXNAMLEN */
  81.  
  82. #ifndef    MAXNAMLEN
  83. #include    <limits.h>
  84. #endif    /* MAXNAMLEN */
  85.  
  86. # define    FSLENGTH    16
  87. # define    PADLENGTH    256L
  88. # define    FINDERPAD    0xE0
  89.  
  90. /*
  91. ** This is what the header looks like on the disk.
  92. */
  93. struct    diskheader
  94. {
  95.     long    disk_magic;
  96.     long    disk_version;
  97.     char    empty[FSLENGTH];    /* leave blank -  this is only used in version 1 */
  98.     short    disk_numentries;
  99. };
  100.  
  101. /*
  102. ** The sizeof operator does not always returned the value that
  103. ** we expect for the above structure. The APPLESINGLE header
  104. ** defines that the above items be present at the start of the
  105. ** file and that a long is a 68000 long word (4 bytes) and a
  106. ** short is a 68000 short word (2 bytes). The sizeof operator
  107. ** on some systems will return the size of the above struture,
  108. ** long word aligned, when will be 2 more bytes that the actual
  109. ** data that will be contained in the APPLESINGLE header itself.
  110. ** This problem happens on SUN SPARCSTATIONS (as well as others).
  111. */
  112. #define    SIZEOF_DISKHEADER    (sizeof(long)+sizeof(long)+FSLENGTH+sizeof(short))
  113.  
  114. /*
  115. ** This is what the entry structure looks like on disk (it directly
  116. ** follows the diskheader).
  117. */
  118. struct    appleentry
  119. {
  120.     unsigned    long    entry_id;
  121.     unsigned    long    entry_offset;
  122.     unsigned    long    entry_length;
  123. };
  124. typedef    struct    diskheader    diskheader;
  125. typedef    struct    appleentry    appleentry;
  126.  
  127. /*
  128. ** This structure is put together out of the above two strctures.
  129. ** This is what is visible to the rest of the world.
  130. */
  131. struct    appleheader
  132. {
  133.     diskheader        header_disk;    
  134.     appleentry        *header_entry;
  135.     int            header_fd;            /* descriptor open to the resource file */
  136.     int            header_datafd;        /* descriptor open to the data file (if it exists) */
  137.     long            header_mask;        /* Keep track of which entries we are reading */
  138.     struct    stat        header_stat;        /* This is used to keep track of the size of the data file. */
  139.     char            *header_dataname;    /* Name of the data file */
  140. };
  141.  
  142. typedef    struct    appleheader    appleheader;
  143.  
  144. /*
  145. ** Macros to reduce typing...
  146. */
  147. # define    header_magic    header_disk.disk_magic
  148. # define    header_version    header_disk.disk_version
  149. # define    header_numentries    header_disk.disk_numentries
  150.  
  151. /*
  152. ** The current magic numbers
  153. */
  154. # define    APPLESINGLE    0x00051600
  155. # define    APPLEDOUBLE    0x00051607
  156. # define    APPLEUNKNOWN    0x00000000
  157.  
  158. /*
  159. ** The only allowed version number.
  160. */
  161. # define    VERSION        0x00010000
  162. # define    VERSION_2    0x00020000
  163.  
  164. /*
  165. ** Macros to test for types.
  166. */
  167. # define    ISSINGLE(hdr)    ((hdr->header_magic) == APPLESINGLE)
  168. # define    ISDOUBLE(hdr)    ((hdr->header_magic) == APPLEDOUBLE)
  169. # define    ISAPPLE(hdr)    ((ISSINGLE(hdr)) || (ISDOUBLE(hdr)))
  170.  
  171. /*
  172. ** Legal entry ids.
  173. */
  174. # define    ENTRY_DATAFORK        1
  175. # define    ENTRY_RESOURCEFORK    2
  176. # define    ENTRY_REALNAME        3
  177. # define    ENTRY_COMMENT        4
  178. # define    ENTRY_ICONBW        5
  179. # define    ENTRY_ICONCOLOR        6
  180. # define    ENTRY_FILEINFO        7
  181. # define    ENTRY_FILES_DATE_INFO      8
  182. # define    ENTRY_FINDERINFO    9
  183.  
  184. # define    ENTRY_MAX        10
  185.  
  186. # ifndef    min
  187. # define    min(a,b)    ((a) < (b) ? (a) : (b))
  188. # endif
  189.  
  190. /*
  191. ** This string defines the type of the following entry in the
  192. ** "packed" file. The types are one of:
  193. **    "PMag"    Uncompressed file
  194. **    "PMa4"    Compressed file
  195. **    "PEnd"    End of file.
  196. */
  197. typedef    char    ENCLHEADER [4];
  198.  
  199. # define    PACK_END    "PEnd"
  200. # define    PACK_COMP    "PMa4"
  201. # define    PACK_UNCOMP    "PMag"
  202.  
  203. /*
  204. ** Header for packed enclosure files. This is the structure that
  205. ** is put at the beginning of each entry in the file. 
  206. **
  207. ** NOTE: Because the 386 does QUAD alignment, I had to remove
  208. ** the CRC short at the end. Also, this structure does not include
  209. ** the "type" header (above) which is also on each entry.
  210. */
  211. typedef    struct
  212. {
  213.     char    fileNameSize;
  214.     char    fileName[63];
  215.     char    fileType[4];
  216.     char    fileCreator[4];
  217.     short    flags;
  218.     short    locked;
  219.     long    dataSize,
  220.         resourceSize,
  221.         createData,
  222.         modDate;
  223. } ENCL;
  224.  
  225. typedef    short    ENCLCRC;
  226.  
  227.  
  228. #define    CREATOR_LEN    4
  229. #define    TYPE_LEN    4
  230.  
  231. #define    BLANK_TYPE    "    "
  232. #define DEF_CREATOR     "UNKN"    /* Default creator type */
  233. #define DEF_TYPE     "TEXT"    /* Default Type */
  234. #define    DEF_FILE    "single.out"    /* Default output file name */
  235. #define REALNAME    90L    /* where we want to put the realname entry */
  236. #define FINDER        146L    /* where we want to put the finder entry */
  237. #define FINDER_SIZE    32    /* size of the finder entry */
  238. #define    CREATE_MODE    0755    /* Default mode for creat() */
  239.  
  240. /*
  241. ** main
  242. **    Call get_start, and exit appropriately.
  243. */
  244. main(argc, argv)
  245. int    argc;
  246. char    *argv[];
  247. {
  248.     /*
  249.     ** call the UNIX version of this program
  250.     */
  251.     get_start(argc,argv);
  252.     exit(0);
  253. }
  254. /*
  255. ** get_start
  256. **    Parse the arguments passed on the command line. If the arguments make
  257. **    sense, translate the input file.
  258. **
  259. ** Arguments
  260. **    argc    Count of arguments
  261. **    argv    List of arguments
  262. **
  263. ** Returns
  264. **    Nothing
  265. */
  266. get_start(argc,argv)
  267. int        argc;
  268. char    *argv[];
  269. {
  270.     char        *infile;        /* Input file name */
  271.     char        *output;        /* Output file name */
  272.     char        creator[CREATOR_LEN];    /* Creator to use */
  273.     char        type[TYPE_LEN];        /* Type to use */
  274.     int        got_o = 0;        /* Flag for -o */
  275.     int        got_f = 0;        /* Flag for -f */
  276.     int        got_s = 0;        /* Flag for -s */
  277.     int        got_c = 0;        /* Flag for -c */
  278.     int        got_t = 0;        /* Flag for -t */
  279.     int        got_per = 0;        /* Were we passed a file with a '%' at the beginning of the name? */
  280.     int        c;            /* current flag returned by getopt */
  281.     struct stat    st;            /* Used to test for the output file */
  282.     int        magic;            /* Return value from get_magic */
  283.     extern int    opterr;            /* getopt external */
  284.     extern int    optind;            /* getopt external */
  285.     extern char    *optarg;        /* getopt external */
  286.  
  287.  
  288.     /*
  289.     ** copy the default values into the creator and type fields
  290.     */
  291.     strncpy(creator, DEF_CREATOR, sizeof (creator));
  292.     strncpy(type, DEF_TYPE, sizeof (type));
  293.  
  294.     /* 
  295.     ** handle options
  296.     */
  297.     while ( (c = getopt(argc, argv, "so:c:f:t:")) != EOF)
  298.     {
  299.               switch (c)
  300.                 {
  301.         /*
  302.         ** -s
  303.         ** Split the input file into three files,
  304.         ** (1) "output" containing the data fork.
  305.         ** (2) "%output" containing the resource information.
  306.         ** (3) "%houtput" containing only the resource header info.
  307.         */
  308.           case 's':
  309.             got_s++;
  310.             break;
  311.  
  312.         /*
  313.         ** -o output
  314.         ** What to call the output file.
  315.         */
  316.                   case 'o':
  317.             got_o++;
  318.             output = optarg;
  319.                         break;
  320.  
  321.         /*
  322.         ** -c Creator
  323.         ** Set the "Creator" field. This is a 4 byte field used by
  324.         ** Macintoshes. Setting this only makes sense when creating
  325.         ** Macintosh files.
  326.         */
  327.                   case 'c':
  328.             got_c++;
  329.             if (strlen(optarg) < 5)
  330.             {
  331.                 strncpy((char *) creator, BLANK_TYPE, sizeof (creator));
  332.                 strncpy( (char *) creator, optarg, strlen(optarg));
  333.             }
  334.             else
  335.             {
  336.                 fprintf(stderr,
  337.                     "The creator field needs to be 4 characters or less\n\n");
  338.                 usage();
  339.             }
  340.                         break;
  341.  
  342.         /*
  343.         ** -t Type
  344.         ** Set the "Type" field. This is a 4 byte field used by
  345.         ** Macintoshes. Setting this only makes sense when creating
  346.         ** Macintosh files.
  347.         */
  348.                   case 't':
  349.             got_t++;
  350.             if (strlen(optarg) < 5)
  351.             {
  352.                 strncpy((char *) type, BLANK_TYPE, sizeof (type));
  353.                 strncpy((char *) type, optarg, strlen(optarg));
  354.             }
  355.             else
  356.             {
  357.                 fprintf(stderr,
  358.                     "The type field needs to be 4 characters or less\n\n");
  359.                 usage();
  360.             }
  361.                         break;
  362.  
  363.           /*
  364.           ** -h, or any error.
  365.           ** help -- print usage message
  366.           */
  367.                   case 'h':    
  368.               case '?':
  369.                   default:
  370.                         usage();
  371.                 }
  372.         }
  373.  
  374.     /*
  375.     ** Make sure we got an input and an output file if appropriate. 
  376.     ** If the file is applesingle then convert to appledouble.
  377.     ** If the file is appledouble then convert to applesingle.
  378.     ** If the file is an unknown appletype then conver to applesingle.
  379.     ** If the file is packed, then unpack the files as applesingle.
  380.     */
  381.     if ( optind >= argc )
  382.     {
  383.         fprintf(stderr,"An input file name needed\n\n", infile);
  384.         usage();
  385.     }
  386.     if ( optind != (argc - 1) )
  387.     {
  388.         fprintf(stderr,"Only one input file allowed\n\n");
  389.         usage();
  390.     }
  391.     infile = argv[optind];
  392.  
  393.  
  394.     /*
  395.     ** Figure out what kind of file this is. Based on that 
  396.     ** information, translate the file.
  397.     */
  398.     magic = get_magic(infile, &got_per);
  399.  
  400.     /*
  401.     ** Packed APPLESINGLE files do not need an output file, so
  402.     ** we should not check the existance of unneeded files.
  403.     */
  404.     if ( magic != 1 )
  405.     {
  406.         if ( !got_o )
  407.         {
  408.             output = DEF_FILE;
  409.             printf("Using output file %s\n",output);
  410.         }
  411.         if ( stat(output,&st) != -1 )
  412.         {
  413.             fprintf(stderr,
  414.                 "Output file %s already exists\n",output);
  415.             exit(1);
  416.         }
  417.     }
  418.     switch(magic)
  419.     {
  420.         /*
  421.         ** Neither Single, nor double.
  422.         */
  423.         case 0:
  424.             if (got_s)
  425.             {
  426.                 fprintf(stderr,
  427.                     "%s not in Apple Single format: -s option does not apply\n\n", infile);
  428.                 usage();
  429.             }
  430.             make_apple(infile,output,creator,type);
  431.             break;
  432.  
  433.         /*
  434.         ** Apple Single format
  435.         */
  436.         case 1:
  437.             if (got_c)
  438.             {
  439.                 fprintf(stderr,
  440.                     "%s is in Apple Single format: -c option does not apply\n\n", infile);
  441.                 usage();
  442.             }
  443.             if (got_t)
  444.             {
  445.                 fprintf(stderr,
  446.                     "%s is in Apple Single format: -t option does not apply\n\n", infile);
  447.                 usage();
  448.             }
  449.             unmake_apple(infile,output,got_s,got_o);
  450.             break;
  451.         /*
  452.         ** Apple Double format
  453.         */
  454.         case 2:
  455.             if (got_c)
  456.             {
  457.                 fprintf(stderr,
  458.                     "%s is in Apple Double format: -c option does not apply\n\n", infile);
  459.                 usage();
  460.             }
  461.             if (got_t)
  462.             {
  463.                 fprintf(stderr,
  464.                     "%s is in Apple Double format: -t option does not apply\n\n", infile);
  465.                 usage();
  466.             }
  467.             if (got_s)
  468.             {
  469.                 fprintf(stderr,
  470.                     "%s not in Apple Single format: -s option does not apply\n\n", infile);
  471.                 usage();
  472.             }
  473.             double_to_single(infile,output,got_per);
  474.             break;
  475.     }
  476. }
  477.  
  478. /*
  479. ** usage
  480. **
  481. **    This function prints the usage messages, and quits.
  482. **    NOTE: This routine will exit.
  483. */
  484. usage()
  485. {
  486.     fprintf (stderr,
  487.         "usage: single [-s] [-c creator] [-t type] -o output input\n\n");
  488.     fprintf (stderr, " -s    create a seperate header and resource file\n");
  489.     fprintf (stderr, " -c    change the creator field in the finder\n");
  490.     fprintf (stderr, " -t    change the type field in the finder\n");
  491.     fprintf (stderr, " -o    the name of the output file\n");
  492.     exit(1);
  493. }
  494.  
  495. /*
  496. ** get_magic
  497. **    This function determines the format of a file.
  498. **
  499. ** Arguments
  500. **    name    The name of the file to check.
  501. **    got_per    whether the file we got starts with a percent
  502. **        This will be returned to the callee.
  503. **
  504. ** Returns
  505. **    2 if the file is Apple Double
  506. **    1 if the file is Apple Single
  507. **    0 if the file is not an Apple format
  508. */ 
  509. get_magic(name, got_per)
  510. char    *name;
  511. int    *got_per;
  512. {
  513.         register int     fd;        /* Descriptor used to read the file. */
  514.         diskheader     disk;            /* How an Apple single or double format
  515.                                         file is stored on disk. */
  516.     int        size;                    /* Size returned by read. */
  517.     char        temp[MAXPATHLEN];    /* Used to construct the '%' file for
  518.                                         AppleDouble format resource forks. */
  519.  
  520.     /*
  521.     ** open the file and try reading the header information
  522.     */
  523.     if((fd = open(name, O_RDONLY )) == -1)
  524.     {
  525.         fprintf(stderr, "get_magic: open on %s failed: ",name);
  526.         perror("");
  527.         close(fd);
  528.         exit (1);
  529.     }
  530.     if((size = read(fd, &disk, SIZEOF_DISKHEADER)) == -1 )
  531.     {
  532.         perror ("get_magic: read failed");
  533.         close(fd);
  534.         exit (1);
  535.     }
  536.     (void) close(fd);
  537.  
  538.     if ( size < SIZEOF_DISKHEADER )
  539.         return (0);
  540.  
  541.     *got_per = 0;
  542.  
  543.     /*
  544.     ** check that the file is applesingle
  545.     */
  546.         if (disk.disk_magic == APPLESINGLE)
  547.         {
  548.                 return(1);
  549.         }
  550.         else
  551.         {
  552.         /*
  553.         ** if the magic number is apple double, then we must
  554.         ** have gotten the percent file
  555.         */
  556.                 if (disk.disk_magic == APPLEDOUBLE)
  557.                 {
  558.                         *got_per = 1;
  559.                         return(2);
  560.                 }
  561.             else
  562.             {
  563.             /*
  564.             ** other-wise we need to check whether the percent file
  565.             ** (if it exists) is apple double. To do this we
  566.             ** construct a new file name, with a '%' in front.
  567.             */
  568.             doubleName(name,temp,0);
  569.  
  570.                     if ((fd = open(temp, O_RDONLY )) == -1)
  571.                 return (0);
  572.  
  573.             if ( read(fd, &disk, SIZEOF_DISKHEADER) < SIZEOF_DISKHEADER )
  574.             {
  575.                 close(fd);
  576.                 return (0);
  577.             }
  578.             (void) close(fd);
  579.                     if (disk.disk_magic == APPLEDOUBLE)
  580.                             return(2);
  581.                     else
  582.                             return(0);
  583.             }
  584.     }
  585. }
  586.  
  587. /*
  588. ** make_apple
  589. **    This function converts the input file into an Apple Single file.
  590. **    This routine will create a new file "output".
  591. ** 
  592. ** Arguments
  593. **    infile     input file name
  594. **    output    output file name
  595. **    creator    what to put into the creator field in the finder
  596. **    type     what to put into the type field in the finder
  597. **
  598. ** Returns
  599. **    Nothing
  600. */
  601. make_apple(infile,output,creator,type)
  602. char    *infile;
  603. char    *output;
  604. char    *creator;
  605. char    *type;
  606. {
  607.     register int    fd;        /* Descriptor to the new file */
  608.     
  609.     /*
  610.     ** Create and open the output file and open it.
  611.     */
  612.      if ((fd = creat(output,CREATE_MODE)) == -1)
  613.     {
  614.         fprintf(stderr, "make_apple: create of %s failed ",output);
  615.         perror("");
  616.         exit(1);
  617.     }
  618.  
  619.     /* 
  620.     ** Write the header information.
  621.     */
  622.     write_header(fd,infile);
  623.  
  624.     /* 
  625.     ** Write the finder information and data, write_rest will close fd.
  626.     */
  627.     write_rest(fd, infile,  creator, type);
  628. }
  629.  
  630. /*
  631. ** write_header
  632. **    This function writes the Apple Single header information 
  633. **    to the output file.
  634. **
  635. ** Arguments
  636. **    fd     open file descriptor to the output file
  637. **    infile    the file to convert to Apple Single format.
  638. **
  639. ** Returns
  640. **    Nothing
  641. */
  642. write_header(fd,infile)
  643. register int    fd;
  644. char        *infile;
  645. {
  646.     diskheader    disk;        /* On disk header structure for Apple Single */
  647.     appleentry    entry;        /* On disk structure for each entry */
  648.     struct    stat    buf;        /* Used to determine the size of the entry */
  649.     char        *lastPart;
  650.  
  651.     /*
  652.     ** set up the information for the header and write it to the file
  653.     */
  654.     memset(&disk, 0, sizeof(diskheader));
  655.     disk.disk_magic = APPLESINGLE;
  656.     disk.disk_version = VERSION_2;
  657.     disk.disk_numentries = 3;
  658.      if ( write(fd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
  659.     {
  660.         perror("write_header: write failed");
  661.         close(fd);
  662.         exit(1);
  663.     }
  664.  
  665.     /*
  666.     ** set up and write the entry information for the finder
  667.     */
  668.     entry.entry_id = ENTRY_FINDERINFO;
  669.     entry.entry_offset = FINDER;
  670.     entry.entry_length = FINDER_SIZE;
  671.         if ( write(fd, &entry, sizeof(entry)) != sizeof(entry) )
  672.         {
  673.                 perror("write_header: write of finder info failed");
  674.                 close(fd);
  675.                 exit(1);
  676.         }
  677.  
  678.     /*
  679.     ** set up the realname entry
  680.     */
  681.     entry.entry_id = ENTRY_REALNAME;
  682.     entry.entry_offset = REALNAME;
  683.     /*
  684.     ** write the original file name, but only the last
  685.     ** portion of a potentially full pathname.
  686.     */
  687.     if((lastPart = strrchr(infile,'/')) == NULL)
  688.         lastPart = infile;
  689.     else
  690.         lastPart++;
  691.     entry.entry_length = strlen(lastPart);
  692.     if ( write(fd, &entry, sizeof(entry)) != sizeof(entry) )
  693.     {
  694.             perror("write_header: write of finder info failed");
  695.             close(fd);
  696.             exit(1);
  697.     }
  698.         
  699.     /*
  700.     ** set up and write the entry information for the data fork
  701.     */
  702.     entry.entry_id = ENTRY_DATAFORK;
  703.     entry.entry_offset = PADLENGTH;
  704.     if( stat(infile,&buf) == -1)
  705.     {
  706.         perror("write_header: stat failed");
  707.         close(fd);
  708.         exit(1);
  709.     }
  710.     entry.entry_length = buf.st_size;
  711.         if ( write(fd, &entry, sizeof(entry)) != sizeof(entry) )
  712.         {
  713.                 perror("write_header: write data fork failed");
  714.                 close(fd);
  715.                 exit(1);
  716.         }
  717. }
  718.  
  719. /*
  720. ** write_rest
  721. **    This function writes the finder information and data.
  722. **    Note, this function will close fd.
  723. **
  724. ** Arguments
  725. **    fd     open file descriptor to the output file
  726. **    infile    the file to convert to Apple Single format.
  727. **    creator    what to put in the creator field in the finder
  728. **    type    what to put in the type field in the finder
  729. **
  730. ** Returns
  731. **    Nothing
  732. */
  733. write_rest(fd, infile, creator, type)
  734. register int    fd;
  735. char        *infile;
  736. char        *creator;
  737. char        *type;
  738. {
  739.     register int    ifd;        /* Used to read infile */
  740.     register int    len;        /* Return value from read */
  741.     char        buf[BUFSIZ];    /* Used to read infile */
  742.     char        *lastPart;    /* the last part of the name */
  743.  
  744.     /*
  745.     ** First, seek to where the "real name" goes
  746.     */
  747.     if((lseek(fd, REALNAME, 0)) == -1)
  748.     {
  749.             perror("write_rest: lseek to REALNAME offset failed");
  750.             (void) close(fd);
  751.             exit(1);
  752.     }
  753.     /*
  754.     ** write the original file name, but only the last
  755.     ** portion of a potentially full pathname.
  756.     */
  757.     if((lastPart = strrchr(infile,'/')) == NULL)
  758.         lastPart = infile;
  759.     else
  760.         lastPart++;
  761.     if ( write(fd, lastPart, strlen(lastPart)) != strlen(lastPart) )
  762.         {
  763.                 perror("write_rest: write of infile name failed");
  764.                 (void) close(fd);
  765.                 exit(1);
  766.         }
  767.  
  768.     /*
  769.     ** next seek to where the finder information is.
  770.     */
  771.     if((lseek(fd, FINDER, 0)) == -1)
  772.     {
  773.                   perror("write_rest: lseek to FINDER offset failed");
  774.                 (void) close(fd);
  775.                 exit(1);
  776.         }
  777.     /*
  778.     ** write the creator and type information
  779.     */
  780.     if ( write(fd, type, TYPE_LEN) != TYPE_LEN )
  781.         {
  782.                 perror("write_rest: write of type failed");
  783.                 (void) close(fd);
  784.                 exit(1);
  785.         }
  786.     if ( write(fd, creator, CREATOR_LEN) != CREATOR_LEN )
  787.         {
  788.                 perror("write_rest: write of creator failed");
  789.                 (void) close(fd);
  790.                 exit(1);
  791.         }
  792.  
  793.     /*
  794.     ** lseek to the PADLENGTH for the resource piece
  795.     ** TOPS requires this. PADLENGTH is relative to the beginning
  796.     ** of the file.
  797.     */
  798.     if( lseek(fd, PADLENGTH, 0) == -1)
  799.     {
  800.                   perror("write_rest: lseek failed");
  801.                 (void) close(fd);
  802.                 exit(1);
  803.         }
  804.  
  805.     /*
  806.     ** open the input file and write it's contents to the output
  807.     ** file
  808.     */
  809.     if( (ifd = open(infile, O_RDONLY )) == -1)
  810.         {
  811.         fprintf(stderr, "write_rest: open of %s failed ",infile);
  812.         perror("");
  813.                 (void) close(fd); 
  814.                 exit(1); 
  815.         }
  816.  
  817.     /*
  818.     ** Copy the contents of infile to the data fork of the out file.
  819.     */
  820.     for (;;)
  821.     {
  822.         /*
  823.         ** Read the data, <=0 hopefully means EOF.
  824.         */
  825.         if((len = read(ifd, buf, sizeof (buf))) <= 0)
  826.             break;
  827.         /*
  828.         ** Write the data to the output file.
  829.         */
  830.             if( write(fd, buf, len) != len )
  831.             { 
  832.                     perror("write_rest: write of data fork failed");
  833.             (void) close(ifd);
  834.                     (void) close(fd); 
  835.                     exit(1); 
  836.             } 
  837.     }
  838.     (void) close(fd);
  839.     (void) close(ifd);
  840. }
  841.  
  842. /*
  843. ** unmake_apple
  844. **    This function splits the apple single file into the resource and data
  845. **    forks.
  846. **
  847. ** Arguments
  848. **    infile    the file to convert to split
  849. **    output    the name of the output file
  850. **    got_s     whether we got a -s as an option
  851. **    got_s     whether we got a -o as an option
  852. **
  853. ** Returns
  854. **     0 on success
  855. **    -1 on error
  856. */
  857. unmake_apple(infile,output,got_s,got_o)
  858. char    *infile;
  859. char    *output;
  860. int     got_s;
  861. int     got_o;
  862. {
  863.     diskheader    disk;            /* On disk structure of the file */
  864.     register int    fd;            /* Descriptor of the file */
  865.     appleentry    entry[ENTRY_MAX];    /* Each entry (resource, data, etc.) */
  866.     int        i;            /* Index variable */
  867.     int        data_num;        /* Entry for the data fork */
  868.     int        unpack = 0;        /* Does this file need to be unpacked? */
  869.     char        creator[CREATOR_LEN];    /* Creator of the file */
  870.     char        type[TYPE_LEN];        /* Type of the file */
  871.     struct    stat    st;            /* Used to check for output file */
  872.  
  873.     /*
  874.     ** open the file and try reading the header information
  875.     */
  876.     if((fd = open(infile, O_RDONLY )) == -1)
  877.     {
  878.         fprintf(stderr, "unmake_apple: open of %s failed ",infile);
  879.         perror("");
  880.         exit(1);
  881.     }
  882.     /*
  883.     ** Read the disk information in.
  884.     */
  885.     if( read(fd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
  886.     {
  887.         perror ("unmake_apple: read of disk information failed");
  888.         exit(1);
  889.     }
  890.  
  891.     /* 
  892.     ** read all of the entries
  893.     */
  894.     for (i = 0; i < disk.disk_numentries; i++)
  895.     {
  896.         if( read(fd, &entry[i], sizeof(entry[i])) != sizeof(entry[i]) )
  897.         {
  898.             perror("unmake_apple: read of disk entry failed");
  899.             exit (1);
  900.         }
  901.     }
  902.     for (i = 0; i < disk.disk_numentries; i++)
  903.     {
  904.         /* 
  905.         ** save the entry number of the data fork for later use
  906.         ** if the file is packed
  907.         */
  908.         if (entry[i].entry_id == ENTRY_DATAFORK)
  909.         {
  910.             data_num = i;
  911.         }
  912.         /*
  913.         ** determine if the file is in packed format by looking at the
  914.         ** finderinfo
  915.         */
  916.         if (entry[i].entry_id == ENTRY_FINDERINFO)
  917.         {
  918.             if( lseek(fd, entry[i].entry_offset, 0) == -1)
  919.             {
  920.                 perror("unmake_apple: lseek failed");
  921.                 exit (1);
  922.             }
  923.             if( read(fd, creator, sizeof(creator)) == -1)
  924.             {
  925.                 perror("unmake_apple: read of creator failed");
  926.                 exit (1);
  927.             }
  928.             if( read(fd, type, sizeof(type)) == -1)
  929.             {
  930.                 perror("unmake_apple: read of type failed");
  931.                 close(fd);
  932.                 exit (1);
  933.             }
  934.             /*
  935.             ** Check to see if it is in PACKIT format.
  936.             */
  937.             if ( (!(strncmp(creator, "PIT ", 4))) || (!(strncmp(type, "PIT ", 4))))
  938.             {
  939.                 unpack++;
  940.             }
  941.         }
  942.     }
  943.     
  944.     /*
  945.     ** if the file is packed then unpack it.
  946.     */
  947.     if(unpack)
  948.     {
  949.         if ( got_o )
  950.         {
  951.             fprintf(stderr,"Can not specify output file for a packed file\n");
  952.             exit(1);
  953.         }
  954.         unpack_file(fd, entry[data_num]);
  955.         (void) close(fd);
  956.         return(0);
  957.     }
  958.     if ( !got_o )
  959.     {
  960.         output = DEF_FILE;
  961.         printf("Using output file %s\n",output);
  962.     }
  963.     if ( stat(output,&st) != -1 )
  964.     {
  965.         fprintf(stderr,"Output file %s already exists\n",output);
  966.         exit(1);
  967.     }
  968.     
  969.     /* 
  970.     ** create the appropriate files
  971.     */
  972.     make_header(disk, entry, output, got_s);
  973.     make_res_data(fd, disk.disk_numentries, entry, output, got_s);
  974.     if( !got_s )
  975.     {
  976.         pad_resource(output);
  977.     }
  978.     (void) close(fd);
  979.             
  980.     return(0);
  981. }
  982.  
  983. /*
  984. ** make_header
  985. **    This function writes the header information for a file.
  986. **
  987. ** Arguments
  988. **    disk    the disk header information in the input file
  989. **    entry    array of the entry fields of the input file
  990. **    output    the name of the output file
  991. **    got_s     whether we got a -s as an option
  992. **
  993. ** Returns
  994. **    Nothing
  995. */
  996. make_header(disk, entry, output, got_s)
  997. diskheader    disk;
  998. appleentry    entry[ENTRY_MAX];
  999. char        *output;
  1000. int        got_s;
  1001. {
  1002.     register int    fd;        /* File descriptor to new file */
  1003.     char        tmp[MAXPATHLEN];/* Name of the resource fork */
  1004.     short        temp;        /* Temp holder */
  1005.     int        i;        /* index variable */
  1006.     int        have_data = 0;    /* Number of data forks */
  1007.     appleentry    nulls;        /* Empty entry for padding */
  1008.  
  1009.     /*
  1010.     ** if we got a -s then create a file with %h followed by the name
  1011.     ** of the output file. Otherwise, create a file with % followed by the
  1012.     ** name of the ouput file
  1013.     */
  1014.     if (got_s)
  1015.     {
  1016.         doubleName(output,tmp,1);
  1017.         disk.disk_magic = APPLEUNKNOWN;
  1018.     }
  1019.     else
  1020.     {
  1021.         doubleName(output,tmp,0);
  1022.         disk.disk_magic = APPLEDOUBLE;
  1023.     }
  1024.      if ((fd = creat(tmp,CREATE_MODE)) == -1)
  1025.     {
  1026.         fprintf(stderr, "make_header: create of %s failed: ",tmp);
  1027.         perror("");
  1028.         exit(1);
  1029.     }
  1030.  
  1031.     /*
  1032.     ** since we are spilting the file don't write the entry for the data
  1033.     ** but we need to decrement the number of entries
  1034.     */
  1035.     temp = disk.disk_numentries;
  1036.     for(i = 0; i < disk.disk_numentries; i++)
  1037.     {
  1038.         if(entry[i].entry_id == ENTRY_DATAFORK)
  1039.         {
  1040.             have_data++;
  1041.             temp--;
  1042.         }
  1043.     }
  1044.     disk.disk_numentries = temp;
  1045.  
  1046.     /*
  1047.     ** write the disk information to the header
  1048.     */
  1049.      if ( write(fd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
  1050.     {
  1051.         perror("make_header: write of disk structure failed");
  1052.         exit(1);
  1053.     }
  1054.  
  1055.     /*
  1056.     ** write all entries to the header except the data fork
  1057.     */
  1058.     for(i = 0; i < disk.disk_numentries; i++)
  1059.     {
  1060.         if(entry[i].entry_id != ENTRY_DATAFORK)
  1061.         {
  1062.             if( write(fd, &entry[i], sizeof(entry[i])) != sizeof (entry[i]) )
  1063.             {
  1064.                 perror("make_header: write of entries failed");
  1065.                 exit(1);
  1066.             }
  1067.         }
  1068.     }
  1069.     (void) memset((char *)&nulls, sizeof (nulls), '\0');
  1070.     for ( i = disk.disk_numentries ; i < ENTRY_MAX ; i++ )
  1071.     {
  1072.         if ( write(fd,&nulls, sizeof(nulls)) != sizeof(nulls) )
  1073.         {
  1074.             perror("make_header: write of null entry failed");
  1075.             exit(1);
  1076.         }
  1077.     }
  1078.  
  1079.     /*
  1080.     ** restore the number of entries
  1081.     */
  1082.     if(have_data)
  1083.         disk.disk_numentries += have_data;
  1084.  
  1085.     (void) close(fd);
  1086. }
  1087.  
  1088. /*
  1089. ** make_res_data
  1090. **    This function writes the resource and data forks
  1091. **
  1092. ** Arguments
  1093. **    fd        open file descriptor to the input file
  1094. **    numentries    the number of entries in the input file
  1095. **    entry        array of the entry fields of the input file
  1096. **    output        the name of the output file
  1097. **    got_s         whether we got a -s as an option
  1098. **
  1099. ** Returns
  1100. **     0 on success
  1101. **    -1 on error
  1102. */
  1103. make_res_data(fd, numentries, entry, output, got_s)
  1104. register int    fd;
  1105. short        numentries;
  1106. appleentry    entry[ENTRY_MAX];
  1107. char        *output;
  1108. int        got_s;
  1109. {
  1110.     register int    rfd;            /* Resource fork descriptor */
  1111.     register int    dfd;            /* Data fork descriptor */
  1112.     char        *file;            /* Pointer to the file name */
  1113.     char        temp[MAXPATHLEN];    /* Storage for the file name */
  1114.     char        buf[BUFSIZ];        /* Buffer for reading the data */
  1115.     int        i;            /* Index variable */
  1116.     int        no_data = 1;        /* Tells us if anything was ever
  1117.                         ** written to the data fork.
  1118.                         */
  1119.     long        num = 0;        /* Length of each entry */
  1120.     int        len = 0;        /* Length returned by read */
  1121.  
  1122.     doubleName(output,temp,0);
  1123.     file = temp;
  1124.  
  1125.     /* 
  1126.     ** if we got a -s then create and open the %output file
  1127.     ** otherwise, just open it
  1128.     */
  1129.     if(got_s)
  1130.     {
  1131.         if ((rfd = creat(file,CREATE_MODE)) == -1)
  1132.         {
  1133.             fprintf(stderr, "make_res_data: creat of %s failed: ",file);
  1134.             perror("");
  1135.             exit(1);
  1136.         }
  1137.     }
  1138.     else
  1139.     {
  1140.         if((rfd = open(file, O_RDWR )) == -1)
  1141.         {
  1142.             fprintf(stderr, "make_res_data: open of %s failed: ",file);
  1143.             perror("");
  1144.             exit(1);
  1145.         }
  1146.         if ( lseek(rfd,0L,2) == -1 )
  1147.         {
  1148.             fprintf(stderr,"make_res_data: lseek failed: ");
  1149.             perror("");
  1150.             exit(1);
  1151.         }
  1152.     }
  1153.     
  1154.     
  1155.     /*
  1156.     ** create the output file for the data fork
  1157.     */
  1158.     if ((dfd = creat(output,CREATE_MODE)) == -1)
  1159.     {
  1160.         fprintf(stderr, "make_res_data: creat of %s failed: ",output);
  1161.         perror("");
  1162.         exit(1);
  1163.     }
  1164.  
  1165.     /*
  1166.     ** Write all of the data to the %output file unless 
  1167.     ** it is the data fork in which case write it to output file.
  1168.     */
  1169.     for (i = 0; i < numentries; i++)
  1170.     {
  1171.         /* 
  1172.         ** read the data
  1173.         */
  1174.         if( lseek(fd, entry[i].entry_offset, 0) != entry[i].entry_offset )
  1175.             { 
  1176.                   perror("make_res_data: lseek failed"); 
  1177.                     exit(1); 
  1178.             }    
  1179.         /* !!!!always!!! seek to the position to write in the resource. */
  1180.         if( entry[i].entry_id != ENTRY_DATAFORK)
  1181.         {
  1182.             if( lseek(rfd, entry[i].entry_offset, 0) != entry[i].entry_offset )
  1183.                 { 
  1184.                     perror("make_res_data: lseek failed"); 
  1185.                         exit(1); 
  1186.                 }    
  1187.         }
  1188.         num = entry[i].entry_length;
  1189.         while(num)
  1190.         {
  1191.             if((len = read(fd, buf, min(num, sizeof(buf)))) <= 0) 
  1192.                 { 
  1193.                 break;
  1194.                 }        
  1195.  
  1196.             /*
  1197.             ** write the data
  1198.             */
  1199.                     if( entry[i].entry_id == ENTRY_DATAFORK)
  1200.             {
  1201.                 /*
  1202.                 ** if we have the data fork then write to the output
  1203.                 */
  1204.                 no_data = 0;
  1205.                 if( write(dfd, buf, len) != len )
  1206.                 {
  1207.                     perror("make_res_data: write to data fork failed");
  1208.                     exit(1);
  1209.                 }
  1210.             }
  1211.             else
  1212.             {
  1213.                 /*
  1214.                 ** otherwise write to the %output file
  1215.                 */
  1216.                 if( write(rfd, buf, len) != len )
  1217.                 {
  1218.                     perror("make_res_data: write of fork failed");
  1219.                     exit(1);
  1220.                 }
  1221.             }
  1222.             num -= len;
  1223.         }
  1224.     }
  1225.     /* 
  1226.     ** if there is no data fork then unlink the output file
  1227.     */
  1228.     if (no_data)
  1229.         (void) unlink(output);
  1230.     (void) close(fd);
  1231.     (void) close(dfd);
  1232.     (void) close(rfd);
  1233. }
  1234.  
  1235. /*
  1236. ** pad_resource
  1237. **    This function will pad the resource fork to PADLENGTH (256)
  1238. **    if it is smaller than that. This is used only for APPLEDOUBLE files.
  1239. **
  1240. ** Arguments
  1241. **     output    output file name
  1242. **
  1243. ** Returns
  1244. **    Nothing
  1245. */
  1246. pad_resource(output)
  1247. char    *output;
  1248. {
  1249.     int        fd;            /* descriptor to the file */
  1250.     char        temp[MAXPATHLEN];    /* Resource fork name of the file */
  1251.     struct    stat    st;            /* Stat structure for determining the pad length */
  1252.     char        eof;            /* Temp for holding EOF */
  1253.  
  1254.     /*
  1255.     ** append a % to the file name
  1256.     */
  1257.     doubleName(output,temp,0);
  1258.  
  1259.     /*
  1260.     ** open this file and pad it if it is smaller than 256 bytes
  1261.     */
  1262.     if((fd = open(temp, O_RDWR )) == -1)
  1263.     {
  1264.         fprintf(stderr, "pad_resource: open of %s failed: ",temp);
  1265.         perror("");
  1266.         exit(1);
  1267.     }
  1268.         if ( stat(temp,&st ) == -1)
  1269.         {
  1270.         perror("pad_resource: stat of output file failed");
  1271.         exit(1);
  1272.         }
  1273.     if (st.st_size < PADLENGTH)
  1274.     {
  1275.         if ( lseek(fd, PADLENGTH-1, 0) == -1 )
  1276.         {
  1277.             perror("pad_resource: lseek(PADLENGTH - 1) failed");
  1278.             exit(1);
  1279.         }
  1280.         eof = EOF;
  1281.         if ( write(fd,&eof,1) != 1 )
  1282.         {
  1283.             perror("pad_resource: write(EOF) failed");
  1284.             exit(1);
  1285.         }
  1286.     }
  1287.     (void) close(fd);
  1288. }
  1289.  
  1290. /*
  1291. ** This function converts an apple double file into an apple single file
  1292. **
  1293. ** Arguments
  1294. **    infile     the name of the input file
  1295. **     output    output file name
  1296. **    whther the input file starts with a %
  1297. **
  1298. ** Returns
  1299. **     0     on success
  1300. **    -1    on failure
  1301. */
  1302. double_to_single(infile, output, got_per)
  1303. char    *infile;
  1304. char    *output;
  1305. int    got_per;
  1306. {
  1307.     diskheader    disk;            /* On disk layout of file */
  1308.     appleentry    entry[ENTRY_MAX];    /* Structure of each entry */
  1309.         int             fd;            /* File descriptor */
  1310.         int             rfd;            /* Descriptor for resource file */
  1311.         int             ofd;            /* Descriptor for data file */
  1312.     int        i;            /* Index variable */
  1313.     long        num;            /* Number of bytes to write out */
  1314.     int        len;            /* Length returned by read */
  1315.     struct    stat    st;            /* Used to determine number of bytes to write out */
  1316.     char        file[MAXPATHLEN];    /* Name of the output file */
  1317.     char        *file1;            /* Pointer to input file */
  1318.     char        temp1[MAXPATHLEN];    /* Another output file */
  1319.     char        buf[BUFSIZ];        /* Buffer for reading file */
  1320.  
  1321.     file1 = infile;
  1322.         if(got_per)
  1323.         {
  1324.         /*
  1325.         ** we got the file starting with a percent.
  1326.         ** try opening the file without the percent.
  1327.         ** if we can't because the file does not exists then 
  1328.         ** just create the output file and dump the input file into it.
  1329.         ** while changing the magic number from appledouble to
  1330.         ** applesingle
  1331.         */
  1332.         singleName(infile,file);
  1333.                 if ((fd = open(file, O_RDONLY )) == -1)
  1334.                 {
  1335.             if(errno == ENOENT)
  1336.             {
  1337.                 /*
  1338.                 ** create and open the output file
  1339.                 */
  1340.                 if ((ofd = creat(output,CREATE_MODE)) == -1)
  1341.                     {
  1342.                     fprintf(stderr, "double_to_single: creat of %s failed: ",output);
  1343.                     perror("");
  1344.                             exit(1);
  1345.                     }
  1346.  
  1347.                 /* 
  1348.                 ** open the input file
  1349.                 */
  1350.                 if((fd = open(infile, O_RDONLY )) == -1)
  1351.                     {
  1352.                     fprintf(stderr, "double_to_single: open on %s failed: ",infile);
  1353.                     perror("");
  1354.                             exit (1);
  1355.                     }
  1356.  
  1357.                 /*
  1358.                 ** stat the input file
  1359.                 ** so that we can figure out its size
  1360.                 */
  1361.                 if( stat(infile, &st) == -1)
  1362.                     {
  1363.                             perror ("double_to_single: stat failed");
  1364.                             exit (1);
  1365.                     }
  1366.  
  1367.                 if( read(fd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
  1368.                     {
  1369.                             perror ("double_to_single: read of header failed");
  1370.                             exit (1);
  1371.                     }
  1372.  
  1373.                 /*
  1374.                 ** change the magic number to applesingle
  1375.                 */
  1376.                 disk.disk_magic = APPLESINGLE;
  1377.  
  1378.                 /*
  1379.                 ** write the header
  1380.                 */
  1381.                 if( write(ofd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
  1382.                     {
  1383.                             perror ("double_to_single: write of header failed");
  1384.                             exit (1);
  1385.                     }
  1386.  
  1387.                 /*
  1388.                 ** lseek past the header and read the rest
  1389.                 ** into a buffer
  1390.                 */
  1391.                 if( lseek(fd, (long)SIZEOF_DISKHEADER, 0) == -1)
  1392.                     {
  1393.                             perror ("double_to_single: seek past header failed");
  1394.                             exit (1);
  1395.                     }
  1396.                 if( lseek(ofd, (long)SIZEOF_DISKHEADER, 0) == -1)
  1397.                     {
  1398.                             perror ("double_to_single: seek on output file failed");
  1399.                             exit (1);
  1400.                     }
  1401.                 num = st.st_size-SIZEOF_DISKHEADER;
  1402.                 while(num)
  1403.                 {
  1404.                     if((len = read(fd, buf, min(num, sizeof(buf)))) <= 0)
  1405.                         {
  1406.                         break;
  1407.                         }
  1408.  
  1409.                     /*
  1410.                     **  write the buffer 
  1411.                     */
  1412.                     if((write(ofd, buf, len)) != len )
  1413.                         {
  1414.                                 perror ("double_to_single: write of buffer failed");
  1415.                                 exit (1);
  1416.                         }
  1417.                     num -= len;
  1418.                 }
  1419.  
  1420.  
  1421.                 (void) close(fd);
  1422.                 (void) close(ofd);
  1423.                 return(0);
  1424.             }
  1425.             else
  1426.             {
  1427.                 fprintf(stderr, "double_to_single: open on %s failed: ",file);
  1428.                 perror("");
  1429.                             exit(1);
  1430.             }
  1431.                 }
  1432.         }
  1433.         else
  1434.         {
  1435.         /*
  1436.         ** open the input file since it does not start with a percent
  1437.         */
  1438.         strcpy(file, infile);
  1439.         doubleName(infile,temp1,0);
  1440.         file1 = temp1;
  1441.  
  1442.                 if ((fd = open(file, O_RDONLY )) == -1)
  1443.                 {
  1444.             fprintf(stderr, "double_to_single: open on %s failed: ",file);
  1445.             perror("");
  1446.                         exit(1);
  1447.                 }
  1448.                 
  1449.         }
  1450.  
  1451.     /* 
  1452.     ** open the file  starting with the percent
  1453.     ** if it does not exists then exit since the
  1454.     ** file can't be apple double
  1455.     */
  1456.         if ((rfd = open(file1, O_RDONLY )) == -1)
  1457.         {
  1458.         fprintf(stderr, "double_to_single: open on %s failed: ",file1);
  1459.         perror("");
  1460.                    exit(1);
  1461.     }
  1462.  
  1463.     /*
  1464.     ** create and open the output file
  1465.     */
  1466.     if ((ofd = creat(output,CREATE_MODE)) == -1)
  1467.         {
  1468.         fprintf(stderr, "double_to_single: create of %s failed: ",output);
  1469.         perror("");
  1470.                 exit(1);
  1471.         }
  1472.     
  1473.     /*
  1474.     ** read the disk and header info from the resource file
  1475.     */
  1476.     if( read(rfd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
  1477.         {
  1478.             perror ("double_to_single: read of resource header failed");
  1479.                    exit (1);
  1480.         }
  1481.         for (i = 0; i < disk.disk_numentries; i++)
  1482.         { 
  1483.                 if( read(rfd, &entry[i], sizeof(entry[i]))
  1484.                         != sizeof(entry[i]) )
  1485.                 {
  1486.                     perror("double_to_single: read of disk entries failed");
  1487.                     exit (1);
  1488.                 }
  1489.         }
  1490.  
  1491.     /*
  1492.     ** stat the data fork to get its size
  1493.     */
  1494.     if((stat(file,&st)) == -1)
  1495.         {
  1496.                 perror("double_to_single: stat failed");
  1497.                 exit(1);
  1498.         }
  1499.  
  1500.     /*
  1501.     ** set-up the header for the output file and entry info
  1502.     ** to include the data fork
  1503.     */
  1504.     disk.disk_magic = APPLESINGLE;
  1505.     entry[disk.disk_numentries].entry_id = ENTRY_DATAFORK;
  1506.     /*
  1507.     ** the length is the length of the data file
  1508.     */
  1509.     entry[disk.disk_numentries].entry_length = (long)st.st_size;
  1510.     if( stat(file1,&st) == -1)
  1511.         {
  1512.                 perror("double_to_single: stat failed");
  1513.                 exit(1);
  1514.         }
  1515.     /*
  1516.     ** the offset is after size of the resource fork
  1517.     */
  1518.     entry[disk.disk_numentries].entry_offset = st.st_size;
  1519.  
  1520.     /*
  1521.     ** write the header and entry info
  1522.     */
  1523.     disk.disk_numentries++;
  1524.     if ( write(ofd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
  1525.         {
  1526.                 perror("double_to_single: write of disk header failed");
  1527.                 exit(1);
  1528.         }
  1529.         for (i = 0; i < disk.disk_numentries; i++)
  1530.         { 
  1531.             if ( write(ofd, &entry[i], sizeof(entry[i])) != sizeof(entry[i]) )
  1532.             {
  1533.                     perror("double_to_single: write of disk entries failed");
  1534.                     exit(1);
  1535.             }
  1536.     }
  1537.  
  1538.     /*
  1539.     ** write the data and resource forks into the output file
  1540.     */
  1541.         for (i = 0; i < disk.disk_numentries; i++)
  1542.         { 
  1543.         if( lseek(rfd, entry[i].entry_offset, 0) == -1)
  1544.                 {
  1545.                         perror("double_to_single: lseek to entry failed");
  1546.                         exit(1);
  1547.                 }
  1548.                 if( lseek(ofd, entry[i].entry_offset, 0) == -1)
  1549.                 {
  1550.                     perror("double_to_single: lseek on output file to entry failed");
  1551.                     exit(1);
  1552.                 }
  1553.                 /*
  1554.                 ** read the data
  1555.         ** if the entry is data fork then read from the data fork file
  1556.         ** otherwise read from the resource fork file
  1557.                 */
  1558.         num = entry[i].entry_length;
  1559.         while(num)
  1560.         {
  1561.                     if((entry[i].entry_id) == ENTRY_DATAFORK)
  1562.                     { 
  1563.                         if((len = read(fd, buf, min(num, sizeof(buf)))) <= 0)
  1564.                         {
  1565.                             break;
  1566.                         } 
  1567.             }
  1568.             else
  1569.             {
  1570.                         if ((len = read(rfd, buf, min(num,sizeof(buf)))) <= 0)
  1571.                         {
  1572.                     break;
  1573.                         }
  1574.             }
  1575.  
  1576.             /*
  1577.             ** write the buffer
  1578.             */
  1579.                     if( write(ofd, buf, len) != len )
  1580.                     {
  1581.                                perror("double_to_single: write of data failed");
  1582.                                exit(1);
  1583.                     }
  1584.             num -= len;
  1585.         }
  1586.     }
  1587.  
  1588.         (void) close(fd);
  1589.         (void) close(rfd);
  1590.         (void) close(ofd);
  1591.     return(0);
  1592. }
  1593.  
  1594. /*
  1595. ** unpack_file
  1596. **    This function unpacks a packed apple file
  1597. **
  1598. ** Arguments
  1599. **    fd    the file descriptor to the input file
  1600. **     entry    the data fork entry information
  1601. **
  1602. ** Returns
  1603. **     0     on success
  1604. **    -1    on failure
  1605. */
  1606. unpack_file(fd, entry)
  1607. register int    fd;
  1608. appleentry    entry;
  1609. {
  1610.     diskheader    disk;            /* Structure for each file in the packed file */
  1611.     appleentry    newentry[ENTRY_MAX];    /* For the entries in each packed file */
  1612.         ENCL            encl;            /* PACKIT structure for each enclosed file */
  1613.         ENCLCRC         hcrc;            /* CRC for ENCL header */
  1614.         ENCLHEADER      header;            /* Type of each packed file (PACK_COMP,
  1615.                                             PACK_UNCOMP, PACK_END) */
  1616.         char            buf[BUFSIZ];        /* Used to read the file */
  1617.     char        filename[MAXNAMLEN];    /* Output file name */
  1618.     int        tfd;            /* Output file descriptor */
  1619.     long        num;            /* Number of bytes to read/write */
  1620.     int        len = 0;        /* Value returned by read */
  1621.     int        i;            /* Index variable */
  1622.  
  1623.     memset(&disk, 0, sizeof(diskheader));
  1624.     /*
  1625.     ** seek to the data fork
  1626.     */
  1627.     if ( lseek (fd, entry.entry_offset, 0) == -1)
  1628.     {
  1629.         perror("unpack_file: initial lseek failed");
  1630.         exit(1);
  1631.     }
  1632.  
  1633.     /*
  1634.     ** the packed files are seperated by PACK_END
  1635.     ** read the files until we have a PACK_END string
  1636.     */
  1637.     while ( read(fd, header, sizeof(header)) != -1 
  1638.             && (strncmp(header,PACK_END,sizeof (header))))
  1639.     {
  1640.         /*
  1641.         ** we don't support these kinds of packed files
  1642.         */
  1643.             if ( strncmp(header,PACK_UNCOMP,sizeof (header))
  1644.                     && strncmp(header,PACK_COMP,sizeof (header)) )
  1645.             {
  1646.             fprintf(stderr,"Can only understand PMag, and PMa4 packed files\n");
  1647.                     exit(1);
  1648.             }
  1649.  
  1650.         /* 
  1651.         ** read the enclosure information
  1652.         */
  1653.         if( read(fd, &encl, sizeof(encl)) != sizeof(encl) )
  1654.         {
  1655.             perror("unpack_file: read of enclosure info failed");
  1656.             exit(1);
  1657.         }
  1658.         if( read(fd, &hcrc, sizeof(hcrc)) != sizeof(hcrc) )
  1659.         {
  1660.             perror("unpack_file: read of crc failed");
  1661.             exit(1);
  1662.         }
  1663.         
  1664.         /*
  1665.         ** encl.fileName is the realname of the packed file.
  1666.         ** create and open it.
  1667.         */
  1668.         strncpy(filename, encl.fileName, encl.fileNameSize);
  1669.         filename[encl.fileNameSize] = '\0';
  1670.         if ((tfd = creat(filename,CREATE_MODE)) == -1)
  1671.         {
  1672.             fprintf(stderr, "unpack_file: create of %s failed: ",filename);
  1673.             perror("");
  1674.             exit(1);
  1675.         }
  1676.  
  1677.         /*
  1678.         ** set-up and write the disk header info
  1679.         */
  1680.         disk.disk_magic = APPLESINGLE;
  1681.         disk.disk_version = VERSION_2;
  1682.         disk.disk_numentries = 4;
  1683.         if (  write(tfd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
  1684.         {
  1685.             perror("unpack_file: write of disk header failed");
  1686.             exit(1);
  1687.         }
  1688.  
  1689.         /*
  1690.         ** set up the realname entry
  1691.         */
  1692.         newentry[0].entry_id = ENTRY_REALNAME;
  1693.         newentry[0].entry_offset = REALNAME;
  1694.         newentry[0].entry_length = encl.fileNameSize;
  1695.         
  1696.         /*
  1697.         ** set up the finderinfo entry
  1698.         */
  1699.         newentry[1].entry_id = ENTRY_FINDERINFO;
  1700.         newentry[1].entry_offset = FINDER;
  1701.         newentry[1].entry_length = FINDER_SIZE;
  1702.  
  1703.         /*
  1704.         ** set up the resourcefork entry
  1705.         */
  1706.         newentry[2].entry_id = ENTRY_RESOURCEFORK;
  1707.         newentry[2].entry_offset = PADLENGTH;
  1708.         newentry[2].entry_length = encl.resourceSize;
  1709.  
  1710.         /*
  1711.         ** set up the datafork entry
  1712.         */
  1713.         newentry[3].entry_id = ENTRY_DATAFORK;
  1714.         newentry[3].entry_offset = PADLENGTH + encl.resourceSize;
  1715.         newentry[3].entry_length = encl.dataSize;
  1716.  
  1717.         /*
  1718.         ** write all entries to the output
  1719.         */
  1720.         for (i=0; i < disk.disk_numentries; i++)
  1721.         {
  1722.             if( write(tfd, &newentry[i], sizeof(newentry[i])) != sizeof(newentry[i]) )
  1723.             {
  1724.                 perror("unpack_file: write of entry fields failed");
  1725.                 exit(1);
  1726.             }
  1727.         }
  1728.  
  1729.         /*
  1730.         ** write the realname entry data
  1731.         */
  1732.         if( lseek(tfd, newentry[0].entry_offset, 0) == -1)
  1733.         {
  1734.             perror("unpack_file: lseek for realname failed");
  1735.             exit(1);
  1736.         }
  1737.         if( write(tfd,encl.fileName, encl.fileNameSize) != encl.fileNameSize )
  1738.         {
  1739.             perror("unpack_file: write for realname failed");
  1740.             exit(1);
  1741.         }
  1742.  
  1743.         /*
  1744.         ** write the finderinfo entry data
  1745.         */
  1746.         if( lseek(tfd, newentry[1].entry_offset, 0) == -1)
  1747.         {
  1748.             perror("unpack_file: lseek for finder info failed");
  1749.             exit(1);
  1750.         }
  1751.         if( write(tfd, encl.fileType, sizeof(encl.fileType))
  1752.                 != sizeof(encl.fileType) )
  1753.         {
  1754.             perror("unpack_file: write for type info failed");
  1755.             exit(1);
  1756.         }
  1757.         if( write(tfd,encl.fileCreator, sizeof(encl.fileCreator))
  1758.                 != sizeof(encl.fileCreator) )
  1759.         {
  1760.             perror("unpack_file: write for creator info failed");
  1761.             exit(1);
  1762.         }
  1763.  
  1764.         /*
  1765.         ** write the resourcefork entry data
  1766.         */
  1767.         if( lseek(tfd, newentry[3].entry_offset, 0) == -1)
  1768.         {
  1769.             perror("unpack_file: lseek resource fork failed");
  1770.             exit(1);
  1771.         }
  1772.         num = encl.dataSize;
  1773.         while(num)
  1774.         {
  1775.             if((len = read(fd, buf, min(num,sizeof(buf)))) <= 0)
  1776.             {
  1777.                 break;
  1778.             }
  1779.             if( write(tfd, buf, len) != len )
  1780.             {
  1781.                 perror("unpack_file: write for resource fork failed");
  1782.                 exit(1);
  1783.             }
  1784.             num -= len;
  1785.         }
  1786.  
  1787.         if( lseek(tfd, newentry[2].entry_offset, 0) == -1)
  1788.         {
  1789.             perror("unpack_file: lseek for data fork failed");
  1790.             exit(1);
  1791.         }
  1792.         /*
  1793.         ** write the datafork entry data
  1794.         */
  1795.         num = encl.resourceSize;
  1796.         while(num)
  1797.         {
  1798.             if( (len = read(fd, buf,len = min(num, sizeof(buf)))) <= 0)
  1799.             {
  1800.                 break;
  1801.             }
  1802.             if( write(tfd, buf, len) != len )
  1803.             {
  1804.                 perror("unpack_file: write for data fork failed");
  1805.                 exit(1);
  1806.             }
  1807.             num -= len;
  1808.         }
  1809.         
  1810.         /*
  1811.         ** read past the last 2 bytes
  1812.         */
  1813.         if( read(fd, &hcrc, sizeof(hcrc)) != sizeof(hcrc) )
  1814.         {
  1815.             perror("unpack_file: read of final CRC failed");
  1816.             exit(1);
  1817.         }
  1818.         (void) close(tfd);
  1819.     }
  1820. }
  1821.  
  1822. /*
  1823. ** doubleName
  1824. **    Convert a file name into a resource fork name. This means adding
  1825. **    either a '%', or a '%h' to the front of the last component of the
  1826. **    file name (i.e. /a/b/c/file -> /a/b/c/%file).
  1827. **
  1828. ** Arguments
  1829. **    input    The initial file name
  1830. **    output    The place to put the new file name (assumed to have enough space)
  1831. **    hname    Boolean, should the prefix be "%", or "%h"?
  1832. **
  1833. ** Returns
  1834. **    Nothing
  1835. */
  1836. doubleName(input,output,hname)
  1837. char    *input;
  1838. char    *output;
  1839. int    hname;
  1840. {
  1841.     char    *slash;            /* The last '/' in the input file */
  1842.     char    lastname[MAXNAMLEN];
  1843.     char    *prefix;        /* Prefix to change the lastname with */
  1844.     int    len;            /* Length of input name */
  1845.  
  1846.     if ( hname )
  1847.         prefix = "%h";
  1848.     else
  1849.         prefix = "%";
  1850.  
  1851.     if ( (len = strlen(input)) > 1 )
  1852.     {
  1853.         if ( input[len - 1] == '/' )
  1854.             input[len - 1] = 0;
  1855.     }
  1856.     if ( (slash = strrchr(input,'/')) == NULL )
  1857.         sprintf(output,"%s%s",prefix,input);
  1858.     else
  1859.     {
  1860.         /* A copy of the last component of the file name. */
  1861.         strcpy(lastname,slash + 1);
  1862.         strcpy(output,input);
  1863.         slash = &output[slash - input];
  1864.         strcpy(slash + 1,prefix);
  1865.         if ( hname )
  1866.             strcpy(slash + 3,lastname);
  1867.         else
  1868.             strcpy(slash + 2,lastname);
  1869.     }
  1870. }
  1871. /*
  1872. ** singleName
  1873. **    Convert a file name from a resource fork name, into the data fork name.
  1874. **    The '%' prefix is striped off of the file name 
  1875. **    (i.e. /a/b/c/%file -> /a/b/c/file).
  1876. **
  1877. ** Arguments
  1878. **    input    The initial file name
  1879. **    output    The place to put the new file name (assumed to have enough space)
  1880. ** Returns
  1881. **    Nothing
  1882. */
  1883. singleName(input,output)
  1884. char    *input;
  1885. char    *output;
  1886. {
  1887.     char    *percent;        /* Location of the last '%' in the file name. */
  1888.     char    lastname[MAXNAMLEN];
  1889.  
  1890.     if ( (percent = strrchr(input,'%')) == NULL )
  1891.         strcpy(output,input);
  1892.     else
  1893.     {
  1894.         /* A copy of the last component of the file name. */
  1895.         strcpy(lastname,percent + 1);
  1896.         *percent = 0;
  1897.         strcpy(output,input);
  1898.         strcat(output,lastname);
  1899.         *percent = '%';
  1900.     }
  1901. }
  1902.  
  1903.  
  1904.